package nodes;

import java.util.Iterator;
import java.util.ListIterator;

import compiler.Compiler;
import compiler.SymbolTable;

import descriptors.AbstractDescr;
import descriptors.ConstDescr;
import descriptors.ProcedureDescr;
import descriptors.VarDescr;

public class ProcedureCallNode extends StatementNode {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private IdentNode ident;
	private ListNode<ExpressionNode> actualParameters;
	
	public ProcedureCallNode(IdentNode ident,ListNode<ExpressionNode> actualParameters,int line,int column){
		super(line,column);
		this.ident=ident;
		this.actualParameters=actualParameters;
	}
	
	@Override
	public AbstractDescr compile(
			SymbolTable symbolTable, Compiler compiler) {
		ProcedureDescr procDescr=(ProcedureDescr)symbolTable.search(compiler.level(),ident.ident());
		if(procDescr==null){
			System.out.println("Compiler level:"+compiler.level());
			System.out.println("Ident: "+ident.ident());
			symbolTable.print();
		}
		else if(!procDescr.params().isEmpty()){
		ListIterator<ExpressionNode> actualIterator=actualParameters.toList().listIterator(actualParameters.size());
		ListIterator<AbstractDescr> formalIterator=procDescr.params().listIterator(procDescr.params().size());
		while(actualIterator.hasPrevious()){
			ExpressionNode expression=actualIterator.previous();
			AbstractDescr actualDescr=expression.compile(symbolTable,compiler);
			AbstractDescr formalDescr=formalIterator.previous();
			// TODO: Check if another check for VarDescr is needed
			if(actualDescr instanceof ConstDescr) compiler.write("PUSHI, "+((ConstDescr)actualDescr).value());
			if(actualDescr instanceof VarDescr && ((VarDescr)actualDescr).isVar()){
			    compiler.write("CONT, "+((VarDescr)actualDescr).getSize());
			}
			if(actualDescr instanceof VarDescr && !((VarDescr)formalDescr).isVar()){
				compiler.write("CONT, "+((VarDescr)formalDescr).getSize());
			}
			int size= formalDescr.getSize();
			compiler.write("GETSP");
			compiler.write("ASSIGN, "+size);
			compiler.write("GETSP");
			compiler.write("PUSHI, "+size);
			compiler.write("ADD");
			compiler.write("SETSP");
		}
		}
		compiler.write("CALL, "+procDescr.start());
		return null;
	}

	@Override
	public void print() {
		trace("ProcedureCallNode");
		ident.print();
		actualParameters.print();
		unindent();
	}

}
